package controllers

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"devOpsApi/base"
	"devOpsApi/common/go-curl"
	"devOpsApi/common/sonar"
	"devOpsApi/common/util"
	"devOpsApi/config/ymlgo"
	"devOpsApi/libs"
	"devOpsApi/middleware"
	"devOpsApi/model"
	v1Model "devOpsApi/version/v1.0/model"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/kataras/iris"
	"github.com/xanzy/go-gitlab"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

type PipelineController struct {
	base.BController
	lock          sync.RWMutex
	StartTime     time.Time
	Monitor       string
	RunPosition   string
	once          sync.Once
	currentUserId string
	currentUid    uint
	FullLogPath   string
	logsId        uint
	accessToken   string
	gitCommit     string
	productPath   string
}

//流水线列表
func (c *PipelineController) GetList() {
	page, err := strconv.Atoi(c.Ctx.URLParam("page"))
	if err != nil || page < 1 {
		page = 1
	}
	pageSize, err := strconv.Atoi(c.Ctx.URLParam("pageSize"))
	if err != nil || pageSize < 1 {
		pageSize = 20
	}
	k := c.Ctx.URLParamDefault("k", "")
	pipelineModel := model.NewPipeline()
	list, total, pages := pipelineModel.List(uint(middleware.GetTokenUId(c.Ctx)), uint(page), uint(pageSize), k)
	for k, v := range list {
		containerName := v.Language + "_" + strconv.Itoa(int(v.Uid)) + "_" + strconv.Itoa(int(v.ID))
		list[k].ContainerName = containerName
	}
	c.OutDataJson(iris.Map{"list": list, "total": total, "pages": pages, "currentPage": page}, iris.StatusOK, "success")
	return
}

//流水线列表
func (c *PipelineController) GetPublicList() {
	pipelineModel := model.NewPipeline()
	list := pipelineModel.ListAll(uint(middleware.GetTokenUId(c.Ctx)))
	c.OutDataJson(iris.Map{"list": list}, iris.StatusOK, "success")
	return
}

//流水线创建
func (c *PipelineController) PostCreate() {
	pipelineModel := model.NewPipeline()
	if err := c.Ctx.ReadJSON(pipelineModel); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	if pipelineModel.SettingConsulUrl != "" {
		_, err := util.NewConsul(pipelineModel.SettingConsulUrl)
		if err != nil {
			c.OutDataJson("", iris.StatusBadRequest, "Consul 状态配置异常! :"+err.Error())
			return
		}
	}

	pipeline, err := pipelineModel.Create(uint(middleware.GetTokenUId(c.Ctx)))
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	uid := int(middleware.GetTokenUId(c.Ctx))
	if pipeline.Monitor == "auto" {
		config := ymlgo.NewConfigSysBase()
		GitLabSecret := util.GetRandomString(16)
		hookId := 0
		//第三方仓库了
		if pipeline.CustomGitUrl != "" {
			_urlParse, err := url.Parse(pipeline.CustomGitUrl)
			if err != nil {
				c.OutDataJson("", iris.StatusBadRequest, err.Error())
				return
			}

			switch _urlParse.Host {
			case "gitee.com":
				gitee, err := model.NewGitee(pipeline.CustomGitUrl)
				if err != nil {
					_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookUrl := config.Domain + "v1/run/gitee/" + strconv.Itoa(int(pipeline.ID))
				ProjectHook, err := gitee.CreateWebHook(hookUrl, GitLabSecret)
				if err != nil {
					_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookId = int(ProjectHook.ID)
			case "github.com":
				github, err := model.NewGithub(pipeline.CustomGitUrl)
				if err != nil {
					_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookUrl := config.Domain + "v1/run/github/" + strconv.Itoa(int(pipeline.ID)) + "?token=" + GitLabSecret
				ProjectHook, err := github.CreateWebHook(hookUrl, GitLabSecret)
				if err != nil {
					_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookId = int(ProjectHook.ID)
			default:
				gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
				hookUrl := config.Domain + "v1/run/" + strconv.Itoa(int(pipeline.ID))
				HookOptions := &gitlab.AddProjectHookOptions{
					URL:                   gitlab.String(hookUrl),
					PushEvents:            gitlab.Bool(false),
					MergeRequestsEvents:   gitlab.Bool(true),
					EnableSSLVerification: gitlab.Bool(false),
					Token:                 gitlab.String(GitLabSecret),
				}
				ProjectHook, _, err := gitModel.Client.Projects.AddProjectHook(int(pipeline.Pid), HookOptions)
				if err != nil {
					_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookId = int(ProjectHook.ID)
			}
		} else {
			//本地仓库
			gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
			hookUrl := config.Domain + "v1/run/" + strconv.Itoa(int(pipeline.ID))
			HookOptions := &gitlab.AddProjectHookOptions{
				URL:                   gitlab.String(hookUrl),
				PushEvents:            gitlab.Bool(false),
				MergeRequestsEvents:   gitlab.Bool(true),
				EnableSSLVerification: gitlab.Bool(false),
				Token:                 gitlab.String(GitLabSecret),
			}
			ProjectHook, _, err := gitModel.Client.Projects.AddProjectHook(int(pipeline.Pid), HookOptions)
			if err != nil {
				_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
				c.OutDataJson("", iris.StatusBadRequest, err.Error())
				return
			}
			hookId = int(ProjectHook.ID)
		}
		if hookId == 0 {
			_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
			c.OutDataJson("", iris.StatusBadRequest, "hookId is 0")
			return
		}
		err = pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), pipeline.ID, map[string]interface{}{"hook_id": hookId, "gitlabsecret": GitLabSecret})
		if err != nil {
			_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
			c.OutDataJson("", iris.StatusBadRequest, err.Error())
			return
		}
	}
	c.OutDataJson(pipeline.ID, iris.StatusOK, "success")
	return
}

//流水线修改详情部分
func (c *PipelineController) PostUpdateDetail() {
	pipelineModel := model.NewPipeline()
	if err := c.Ctx.ReadJSON(pipelineModel); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	if pipelineModel.ID < 1 {
		c.OutDataJson("", iris.StatusBadRequest, "ID error")
		return
	}
	if pipelineModel.Flow == "" {
		c.OutDataJson("", iris.StatusBadRequest, "flow is empty")
		return
	}
	Flow, _ := json.Marshal(pipelineModel.Flow)
	pipelineModel.Flow = string(Flow)

	err := pipelineModel.UpdateFieldsAnyOne(pipelineModel.ID, map[string]interface{}{"flow": pipelineModel.Flow})
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//流水线修改
func (c *PipelineController) PostUpdate() {
	pipelineModel := model.NewPipeline()
	if err := c.Ctx.ReadJSON(pipelineModel); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	if pipelineModel.SettingConsulUrl != "" {
		_, err := util.NewConsul(pipelineModel.SettingConsulUrl)
		if err != nil {
			c.OutDataJson("", iris.StatusBadRequest, "Consul 状态配置异常! :"+err.Error())
			return
		}
	}

	err := pipelineModel.Update(uint(middleware.GetTokenUId(c.Ctx)))
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), pipelineModel.ID)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	uid := int(middleware.GetTokenUId(c.Ctx))
	if pipeline.Monitor == "auto" {
		config := ymlgo.NewConfigSysBase()
		GitLabSecret := util.GetRandomString(16)
		if pipeline.HookId == 0 { //自动模式且不存在hook 增加hook
			hookId := 0
			//第三方仓库
			if pipeline.CustomGitUrl != "" {
				_urlParse, err := url.Parse(pipeline.CustomGitUrl)
				if err != nil {
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}

				switch _urlParse.Host {
				case "gitee.com":
					gitee, err := model.NewGitee(pipeline.CustomGitUrl)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					hookUrl := config.Domain + "v1/run/gitee/" + strconv.Itoa(int(pipeline.ID))
					ProjectHook, err := gitee.CreateWebHook(hookUrl, GitLabSecret)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					hookId = int(ProjectHook.ID)
				case "github.com":
					github, err := model.NewGithub(pipeline.CustomGitUrl)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					hookUrl := config.Domain + "v1/run/github/" + strconv.Itoa(int(pipeline.ID)) + "?token=" + GitLabSecret
					ProjectHook, err := github.CreateWebHook(hookUrl, GitLabSecret)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					hookId = int(ProjectHook.ID)
				default:
					gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
					hookUrl := config.Domain + "v1/run/" + strconv.Itoa(int(pipeline.ID))
					HookOptions := &gitlab.AddProjectHookOptions{
						URL:                   gitlab.String(hookUrl),
						PushEvents:            gitlab.Bool(false),
						MergeRequestsEvents:   gitlab.Bool(true),
						EnableSSLVerification: gitlab.Bool(false),
						Token:                 gitlab.String(GitLabSecret),
					}
					ProjectHook, _, err := gitModel.Client.Projects.AddProjectHook(int(pipeline.Pid), HookOptions)
					if err != nil {
						//_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					hookId = int(ProjectHook.ID)
				}
			} else {
				//内部仓库
				gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
				hookUrl := config.Domain + "v1/run/" + strconv.Itoa(int(pipeline.ID))
				HookOptions := &gitlab.AddProjectHookOptions{
					URL:                   gitlab.String(hookUrl),
					PushEvents:            gitlab.Bool(false),
					MergeRequestsEvents:   gitlab.Bool(true),
					EnableSSLVerification: gitlab.Bool(false),
					Token:                 gitlab.String(GitLabSecret),
				}
				ProjectHook, _, err := gitModel.Client.Projects.AddProjectHook(int(pipeline.Pid), HookOptions)
				if err != nil {
					//_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				hookId = int(ProjectHook.ID)
			}

			if hookId == 0 {
				c.OutDataJson("", iris.StatusBadRequest, "hookId is 0")
				return
			}
			err = pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), pipeline.ID, map[string]interface{}{"hook_id": hookId, "gitlabsecret": GitLabSecret})
			if err != nil {
				//_ = pipelineModel.DelReal(uint(uid), pipeline.ID)
				c.OutDataJson("", iris.StatusBadRequest, err.Error())
				return
			}
		}
	} else if pipeline.Monitor == "manul" || pipeline.Monitor == "cron" {
		if pipeline.HookId > 0 { //手动模式且存在hook 删除hook
			//第三方仓库
			if pipeline.CustomGitUrl != "" {
				_urlParse, err := url.Parse(pipeline.CustomGitUrl)
				if err != nil {
					c.OutDataJson("", iris.StatusBadRequest, err.Error())
					return
				}
				switch _urlParse.Host {
				case "gitee.com":
					gitee, err := model.NewGitee(pipeline.CustomGitUrl)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					err = gitee.DeleteWebHook(strconv.Itoa(int(pipeline.HookId)))
					if err != nil {
						//修改git地址后,由于不存在webhook 会导致删除hook报错
						//c.OutDataJson("", iris.StatusBadRequest, err.Error())
						//return
					}
				case "github.com":
					github, err := model.NewGithub(pipeline.CustomGitUrl)
					if err != nil {
						c.OutDataJson("", iris.StatusBadRequest, err.Error())
						return
					}
					err = github.DeleteWebHook(strconv.Itoa(int(pipeline.HookId)))
					if err != nil {
						//c.OutDataJson("", iris.StatusBadRequest, err.Error())
						//return
					}
				default:
					gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
					_, err := gitModel.Client.Projects.DeleteProjectHook(int(pipeline.Pid), int(pipeline.HookId))
					if err != nil {
						//c.OutDataJson("", iris.StatusBadRequest, err.Error())
						//return
					}
				}
			} else {
				//内部仓库
				gitModel, _ := model.NewUserGitlab(uid, pipeline.CustomGitUrl)
				_, err := gitModel.Client.Projects.DeleteProjectHook(int(pipeline.Pid), int(pipeline.HookId))
				if err != nil {
					//c.OutDataJson("", iris.StatusBadRequest, err.Error())
					//return
				}
			}

			err = pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), pipeline.ID, map[string]interface{}{"hook_id": 0, "gitlabsecret": ""})
			if err != nil {
				c.OutDataJson("", iris.StatusBadRequest, err.Error())
				return
			}
		}
	}
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//流水线拷贝
func (c *PipelineController) PostCopyBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	rs, err := pipelineModel.Copy(uint(middleware.GetTokenUId(c.Ctx)), pipeline)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(rs, iris.StatusOK, "success")
	return
}

//流水线详情
func (c *PipelineController) GetDetailBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)

	if err == nil {
		if pipeline.HookId > 0 {
			gitModel, _ := model.NewUserGitlab(int(middleware.GetTokenUId(c.Ctx)), pipeline.CustomGitUrl)
			projectHook, _, err := gitModel.Client.Projects.GetProjectHook(int(pipeline.Pid), int(pipeline.HookId))
			if err == nil && projectHook.URL != "" {
				//真实有效的hoookid
				pipeline.HookId = uint(projectHook.ID)
			}
		}
		c.OutDataJson(pipeline, iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//流水线删除详情
func (c *PipelineController) PostDelDetailBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "记录不存在!")
		return
	}
	err = pipelineModel.UpdateFieldsAnyOne(pipeline.ID, map[string]interface{}{"flow": ""})
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//流水线删除
func (c *PipelineController) PostDelBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "记录不存在!")
		return
	}
	err = pipelineModel.Del(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	if pipeline.CustomGitUrl != "" {
		_urlParse, err := url.Parse(pipeline.CustomGitUrl)
		if err != nil {
			c.OutDataJson("", iris.StatusBadRequest, err.Error())
			return
		}
		switch _urlParse.Host {
		case "gitee.com":
			gitee, err := model.NewGitee(pipeline.CustomGitUrl)
			if err != nil {
				//c.OutDataJson("", iris.StatusBadRequest, err.Error())
				//return
			}
			_ = gitee.DeleteWebHook(strconv.Itoa(int(pipeline.HookId)))
		case "github.com":
			github, err := model.NewGithub(pipeline.CustomGitUrl)
			if err != nil {
				//c.OutDataJson("", iris.StatusBadRequest, err.Error())
				//return
			}
			_ = github.DeleteWebHook(strconv.Itoa(int(pipeline.HookId)))
		default:
			gitModel := model.NewGitlab()
			_, _ = gitModel.Client.Projects.DeleteProjectHook(int(pipeline.Pid), int(pipeline.HookId))
		}
	} else {
		gitModel := model.NewGitlab()
		_, _ = gitModel.Client.Projects.DeleteProjectHook(int(pipeline.Pid), int(pipeline.HookId))
	}

	err = pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), pipeline.ID, map[string]interface{}{"hook_id": 0, "gitlabsecret": ""})

	go func() {
		//容器名称
		containerName := c.createContainerName(pipeline)
		//映射目录
		proFolder := c.getProFolder(pipeline)
		cli := util.NewDockerCli()
		defer cli.Client.Close()
		_ = cli.Client.ContainerStop(cli.Ctx, containerName, nil)
		_ = cli.Client.ContainerRemove(cli.Ctx, containerName, types.ContainerRemoveOptions{})
		_ = os.RemoveAll(proFolder)

		//sonar删除
		SonarObj, _ := v1Model.NewSonar()
		_ = SonarObj.ProjectDel(pipeline.Name)

	}()
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//流水线清理
func (c *PipelineController) PostCleanBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "记录不存在!")
		return
	}
	_ = pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), id, map[string]interface{}{"success_num": 0, "error_num": 0, "error_msg": "", "build_state": "未构建"})
	pipelineLogsModel := model.NewPipelineLogs()
	_ = pipelineLogsModel.DelByPid(pipeline.ID)
	c.OutDataJson("", iris.StatusOK, "success")
	go func() {
		//容器名称
		containerName := c.createContainerName(pipeline)
		//映射目录
		proFolder := c.getProFolder(pipeline)
		cli := util.NewDockerCli()
		defer cli.Client.Close()
		_ = cli.Client.ContainerStop(cli.Ctx, containerName, nil)
		_ = cli.Client.ContainerRemove(cli.Ctx, containerName, types.ContainerRemoveOptions{})
		_ = os.RemoveAll(proFolder)
	}()
	return
}

//流水线停止
func (c *PipelineController) PostStopBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "记录不存在!")
		return
	}
	containerName := c.createContainerName(pipeline)
	cli := util.NewDockerCli()
	defer cli.Client.Close()
	//go func() {
	_ = cli.Client.ContainerStop(cli.Ctx, containerName, nil)
	//}()
	c.RunSendInfo("流水线主动停止", "err", pipeline)
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//上传文件
func (c *PipelineController) PostPublicUpload() {
	uid := uint(middleware.GetTokenUId(c.Ctx))
	rs, filePath := util.UploadPipeLineFile("file", strconv.Itoa(int(uid)), c.Ctx)
	if rs == false {
		c.OutDataJson("", iris.StatusBadRequest, filePath)
		return
	}
	_, short_name := filepath.Split(filePath)
	pipelineFileModel := model.NewPipelineFile()
	pipelineFileModel.Uid = uid
	pipelineFileModel.LocalFile = filePath
	pipelineFileModel.ShortName = short_name
	pipelineFileModel.Md5 = util.BeeMd5(filePath)
	if err := pipelineFileModel.Create(); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	last, _ := pipelineFileModel.Last()
	c.OutDataJson(last, iris.StatusOK, "success")
}

//项目文件列表
func (c *PipelineController) GetPublicUploadList() {
	page, err := strconv.Atoi(c.Ctx.URLParam("page"))
	if err != nil || page < 1 {
		page = 1
	}
	pageSize, err := strconv.Atoi(c.Ctx.URLParam("pageSize"))
	if err != nil || pageSize < 1 {
		pageSize = 20
	}
	uid := uint(middleware.GetTokenUId(c.Ctx))
	key := c.Ctx.URLParamDefault("key", "")
	pipelineFileModel := model.NewPipelineFile()
	list, total, pages := pipelineFileModel.List(uid, uint(page), uint(pageSize), key)
	pwdDir, _ := os.Getwd()
	for k, v := range list {
		list[k].LocalFile = path.Clean(pwdDir + "/" + v.LocalFile[1:])
	}
	c.OutDataJson(iris.Map{"list": list, "total": total, "pages": pages, "currentPage": page}, iris.StatusOK, "success")
}

//项目文件列表
func (c *PipelineController) GetPublicUploadListAll() {
	uid := uint(middleware.GetTokenUId(c.Ctx))
	pipelineFileModel := model.NewPipelineFile()
	list := pipelineFileModel.ListAll(uid)
	pwdDir, _ := os.Getwd()
	for k, v := range list {
		list[k].LocalFile = path.Clean(pwdDir + "/" + v.LocalFile[1:])
		list[k].ShortName = v.RealName + " -> " + v.ShortName
	}
	c.OutDataJson(iris.Map{"list": list}, iris.StatusOK, "success")
}

//运行流水线
func (c *PipelineController) PostRunBy(id uint) {
	c.accessToken = c.Ctx.GetHeader("access_token")
	c.currentUserId = middleware.GetTokenUserId(c.Ctx)
	c.currentUid = uint(middleware.GetTokenUId(c.Ctx))

	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(c.currentUid, id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	//记录日志
	PipelineLogs := model.NewPipelineLogs()
	userModel := model.NewUser()
	userInfo, _ := userModel.GetUserByUserId(c.currentUserId)
	PipelineLogs.UserId = userInfo.UserId
	PipelineLogs.RealName = userInfo.RealName
	PipelineLogs.Coderepo = pipeline.Coderepo
	if pipeline.CustomGitUrl != "" {
		PipelineLogs.Coderepo = pipeline.CustomGitUrl
	}
	PipelineLogs.Branch = pipeline.Branch
	PipelineLogs.Pid = pipeline.ID
	PipelineLogs.Cate = 3
	insertId, err := PipelineLogs.Create()
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	LogPath := "./uploads/PipelineLogs/" + strconv.Itoa(int(pipeline.Uid)) + "/"
	c.FullLogPath = LogPath + pipeline.Name + "_" + strconv.Itoa(int(insertId)) + ".log"
	err = util.Tracefile(c.FullLogPath, "")
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	err = PipelineLogs.UpdateFields(uint(insertId), map[string]interface{}{"message": c.FullLogPath})
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.logsId = insertId

	//todo 开放平台定制
	if c.accessToken != "" {
		//更新当前全局变量envs中的access_token为最新值
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), ": UpdateConfigEnv access_token:", c.accessToken)
		pipelineModel := model.NewPipeline()
		_ = pipelineModel.SetConfigEnvBy(c.currentUid, id, "access_token", c.accessToken)
	}

	//start_callback_url
	if pipeline.StartCallbackUrl != "" {
		//go func() {
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), ": 开始回调地址 access_token:", c.accessToken)
		Resp, err := curl.NewRequest().SetUrl(pipeline.StartCallbackUrl).SetHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded", "access_token": c.accessToken}).SetPostData(map[string]interface{}{"id": strconv.Itoa(int(pipeline.ID)), "name": pipeline.Name, "title": pipeline.Title}).PostUrlencoded()
		if err != nil {
			c.OutDataJson("", iris.StatusBadRequest, err.Error())
			return
		}

		if !Resp.IsOk() {
			c.OutDataJson("", iris.StatusBadRequest, "开始回调地址["+pipeline.StartCallbackUrl+"]调用失败 : "+Resp.Raw.Status)
			return
		}

		//todo 开放平台定制开发,重新设置了全局变量
		pipeline, _ = pipelineModel.Detail(c.currentUid, id)
		//}()
	}

	now := time.Now()
	err = pipelineModel.UpdateFields(c.currentUid, id, map[string]interface{}{"last_build": now})
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	pipeline.LastBuild = &now
	_Type := c.Ctx.URLParamDefault("type", "all")
	_Position := c.Ctx.URLParamDefault("position", "")
	go func() {
		runType := make(map[string]string)
		runType["type"] = _Type         // all:全部执行 step:单点执行 persion:人工卡点,往后执行
		runType["position"] = _Position //当前步骤 1-list-0
		c.RunPipeline(pipeline, "manul", runType)
		return
	}()
	c.OutDataJson(pipeline, iris.StatusOK, "success")
}

//运行流水线
func (c *PipelineController) RunPipeline(pipeline model.Pipeline, monitor string, runType map[string]string) {
	c.RunPosition = "ready"
	//运行时间
	c.StartTime = time.Now()
	c.Monitor = monitor

	//项目目录地址 & 与物理机的映射地址
	proFolder := c.getProFolder(pipeline)

	//初始docker客户端
	c.RunSendInfo("初始docker客户端", "info", pipeline)
	cli := util.NewDockerCli()
	defer cli.Client.Close()

	var err error
	//准备解析json配置数据
	EnvsJson := simplejson.New()
	if string(pipeline.Envs.([]byte)) != "" {
		EnvsJson, err = simplejson.NewJson(pipeline.Envs.([]byte))
		if err != nil {
			c.RunSendInfo("全局参数解析失败："+err.Error(), "err", pipeline)
			return
		}
	}

	SettingFileJson := simplejson.New()
	if string(pipeline.SettingFile.([]byte)) != "" {
		SettingFileJson, err = simplejson.NewJson(pipeline.SettingFile.([]byte))
		if err != nil {
			c.RunSendInfo("配置文件解析失败："+err.Error(), "err", pipeline)
			return
		}
	}

	SettingConsulJson := simplejson.New()
	if string(pipeline.SettingConsul.([]byte)) != "" {
		SettingConsulJson, err = simplejson.NewJson(pipeline.SettingConsul.([]byte))
		if err != nil {
			c.RunSendInfo("配置文件解析失败："+err.Error(), "err", pipeline)
			return
		}
	}

	FlowJson := simplejson.New()
	if string(pipeline.Flow.([]byte)) != "" {
		FlowJson, err = simplejson.NewJson(pipeline.Flow.([]byte))
		if err != nil {
			c.RunSendInfo("工作流解析失败："+err.Error(), "err", pipeline)
			return
		}
	}

	//解析settings数据 转换成map
	nodeList, _ := FlowJson.GetPath("nodeList").Array()

	//容器名称
	containerName := c.createContainerName(pipeline)
	if runType["type"] == "all" {
		//解析env数据 转换成map
		var envData []string
		envList, _ := EnvsJson.Array()
		for _, list := range envList {
			data := list.(map[string]interface{})
			if data["name"] != nil && data["value"] != nil {
				envData = append(envData, data["name"].(string)+"="+data["value"].(string))
				cli.SetEnv(envData)
			}
		}

		//解析SettingFile数据 转换成map
		SettingFileData := make(map[string]string)
		SettingFileList, _ := SettingFileJson.Array()
		pipelineFileModel := model.NewPipelineFile()
		for _, list := range SettingFileList {
			data := list.(map[string]interface{})
			setting_file_id_str := data["setting_file_id"].(string)
			setting_file_id, err := strconv.Atoi(setting_file_id_str)
			if err != nil {
				continue
			}
			if pipelineFile, err := pipelineFileModel.Detail(uint(setting_file_id)); err == nil {
				if util.FileExists(pipelineFile.LocalFile) {
					SettingFileData[pipelineFile.LocalFile[1:]] = data["dockerPath"].(string)
				}
			}
		}

		//解析SettingConsul数据 转换成map
		SettingConsulData := make(map[string]string)
		SettingConsulList, _ := SettingConsulJson.Array()
		for _, list := range SettingConsulList {
			data := list.(map[string]interface{})
			setting_file_id_str := data["setting_file_id"].(string)
			setting_file_id, err := strconv.Atoi(setting_file_id_str)
			if err != nil {
				continue
			}
			if pipelineFile, err := pipelineFileModel.Detail(uint(setting_file_id)); err == nil {
				if util.FileExists(pipelineFile.LocalFile) {
					SettingConsulData[pipelineFile.LocalFile[1:]] = data["settingPath"].(string)
				}
			}
		}

		//获取gitlab token
		userModel := model.NewUser()
		UserDb, _ := userModel.GetUser(pipeline.Uid)
		//GitlabToken, err := c.GitLabToken(&UserDb)
		//组织git地址
		oauth2Url := ""
		if pipeline.CustomGitUrl != "" {
			CustomGitUrlParse, _ := url.Parse(pipeline.CustomGitUrl)
			if CustomGitUrlParse == nil {
				c.RunSendInfo("自定义git仓库地址解析失败，请检查！", "err", pipeline)
				return
			}
			oauth2Url = CustomGitUrlParse.String()
		} else {
			//oauth2Url, _ = c.Oauth2Url(pipeline.Coderepo, GitlabToken)
		}

		//oauth2Url = strings.Replace(oauth2Url,"@","%40",-1)

		//获取镜像名称
		ImageName := pipeline.CustomLanguage
		if pipeline.Language != "CUSTOM" {
			ImageName = cli.GetImageName(pipeline.Language, pipeline.Version)
		}
		if ImageName == "" {
			c.RunSendInfo(pipeline.Language+" 镜像不存在", "err", pipeline)
			return
		}

		//镜像是否存在
		hasLanguageImage := false
		Images, err := cli.Client.ImageList(cli.Ctx, types.ImageListOptions{})
		if err != nil {
			c.RunSendInfo(err.Error(), "err", pipeline)
			return
		}
		for _, v := range Images {
			if len(v.RepoTags) > 0 {
				if v.RepoTags[0] == ImageName {
					//if strings.Contains(v.RepoTags[0], ImageName) {
					hasLanguageImage = true
				}
			}
		}

		//镜像不存在 拉取镜像
		if !hasLanguageImage {
			c.RunSendInfo("拉取镜像["+ImageName+"]", "info", pipeline)

			// 权限验证
			RegistryAuth := ""
			if strings.Contains(ImageName, "hub.xxxx.com.cn") {
				pipeline.CustomAccount = "xxx"
				pipeline.CustomPwd = "xxx"
				authConfig := types.AuthConfig{Username: pipeline.CustomAccount, Password: pipeline.CustomPwd}
				encodedJSON, err := json.Marshal(authConfig)
				if err != nil {
					c.RunSendInfo(err.Error(), "err", pipeline)
					return
				}
				RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
			}

			//拉取镜像
			events, err := cli.Client.ImagePull(cli.Ctx, ImageName, types.ImagePullOptions{All: false, RegistryAuth: RegistryAuth})
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}
			d := json.NewDecoder(events)
			type Event struct {
				Status         string `json:"status"`
				Error          string `json:"error"`
				Progress       string `json:"progress"`
				ProgressDetail struct {
					Current int `json:"current"`
					Total   int `json:"total"`
				} `json:"progressDetail"`
			}

			var event *Event
			for {
				if err := d.Decode(&event); err != nil {
					if err == io.EOF {
						break
					}
				}
				c.RunSendInfo("Docker images Pull:"+event.Status, "info", pipeline)
			}

			if event != nil {
				if strings.Contains(event.Status, fmt.Sprintf("Downloaded newer image for %s", ImageName)) {
					c.RunSendInfo(ImageName+" is new pull", "info", pipeline)
				}

				if strings.Contains(event.Status, fmt.Sprintf("Image is up to date for %s", ImageName)) {
					c.RunSendInfo(ImageName+" up-to-date", "info", pipeline)
				}
			}
		} else {
			c.RunSendInfo("镜像["+ImageName+"] 已存在!", "info", pipeline)
		}

		//容器不存在 则创建
		containerID := ""
		detail, _ := cli.Client.ContainerInspect(cli.Ctx, containerName)
		if detail.ContainerJSONBase == nil { //不存在容器
			c.RunSendInfo("创建容器["+containerName+"]", "info", pipeline)
			resp, err := cli.Client.ContainerCreate(cli.Ctx,
				&container.Config{
					Image: ImageName,
					Cmd:   []string{"/bin/sh", "-ec", "while :; do echo '.'; sleep 5 ; done"},
					//Cmd:          []string{"/bin/bash"},
					Env:          envData,
					WorkingDir:   proFolder, //工作目录 免除每次命令都要cd
					AttachStdin:  true,
					AttachStdout: true,
					AttachStderr: true,
					OpenStdin:    true,
					Tty:          true,
					User:         "root",
				},
				&container.HostConfig{
					//取消bings 节约物理机磁盘
					//Binds: []string{proFolder + ":" + proFolder},
					Resources: container.Resources{
						NanoCPUs: int64(2) * 1e9, // 1核心CPU
						Memory:   1073741824 * 3, //1GB内存
					},
					RestartPolicy: container.RestartPolicy{
						//Name: "always",
					},
					DNS:        []string{"218.30.118.6", "8.8.8.8"},
					Privileged: true,
					ExtraHosts: []string{},
				}, nil, containerName)
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}
			containerID = resp.ID
		} else {
			containerState := detail.ContainerJSONBase.State.Status
			if containerState == "exited" || containerState == "dead" {
				containerID = detail.ContainerJSONBase.ID
			} else {
				c.RunSendInfo("容器正处于【"+detail.ContainerJSONBase.State.Status+"】状态，请稍后重试！", "err", pipeline)
				return
			}
		}

		//启动容器
		c.RunSendInfo("启动容器", "info", pipeline)
		err = cli.Client.ContainerStart(cli.Ctx, containerID, types.ContainerStartOptions{})
		if err != nil {
			c.RunSendInfo(err.Error(), "err", pipeline)
			return
		}

		defer func() {
			go func() {
				_ = cli.Client.ContainerStop(cli.Ctx, containerID, nil)
				_ = cli.Client.Close()
			}()
		}()

		//拷贝配置文件
		if len(SettingFileData) > 0 {
			for localFile, DockerPath := range SettingFileData {
				if DockerPath == "{{项目目录}}" {
					DockerPath = c.getProFolder(pipeline)
				}
				c.RunSendInfo("拷贝配置文件 -> "+DockerPath, "info", pipeline)
				err := cli.CopyToDocker(localFile, DockerPath, containerID)
				if err != nil {
					c.RunSendInfo(err.Error(), "err", pipeline)
					return
				}
			}
		}

		//更新consul配置文件
		if len(SettingConsulData) > 0 && pipeline.SettingConsulUrl != "" {
			consulClient, err := util.NewConsul(pipeline.SettingConsulUrl)
			if err != nil {
				c.RunSendInfo("Consul 状态配置异常! :"+err.Error(), "err", pipeline)
				return
			}
			pwdDir, _ := os.Getwd()
			for localFile, SettingKey := range SettingConsulData {
				c.RunSendInfo("更新consul配置文件 -> "+SettingKey, "info", pipeline)
				SettingVal, err := ioutil.ReadFile(pwdDir + localFile)
				if err == nil {
					if err := consulClient.Set(SettingKey, SettingVal); err != nil {
						c.RunSendInfo("Consul 配置失败! :"+err.Error(), "err", pipeline)
						return
					}
				}
			}
		}

		//检测git是否安装
		gitVersion, err := cli.Run(containerID, "git --version")
		if err != nil || strings.Contains(gitVersion, "command not found") {
			c.RunSendInfo("git未安装,尝试安装git软件", "info", pipeline)

			c.RunSendInfo("拷贝superupdate.sh", "info", pipeline)
			if err := cli.CopyToDocker("/uploads/download/superupdate.sh", "/tmp", containerID); err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}

			_, err = cli.Exec(containerID, "chmod +x /tmp/superupdate.sh && bash /tmp/superupdate.sh aliyun", func(line string) {
				c.RunSendInfo(line, "info", pipeline)
			})
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}

			installCmd := "yum install git -y"
			if _, err := cli.Client.ContainerStatPath(cli.Ctx, containerID, "/usr/bin/yum"); err != nil {
				installCmd = "apt-get install git -y"
			}
			c.RunSendInfo(installCmd, "info", pipeline)
			_, _ = cli.Exec(containerID, installCmd, func(line string) {
				c.RunSendInfo(line, "info", pipeline)
			})

			time.Sleep(time.Second * 1)
			_, err = cli.Run(containerID, "git --version")
			if err != nil {
				c.RunSendInfo("尝试安装git软件失败", "err", pipeline)
				return
			}
		}

		//克隆代码
		c.RunSendInfo("获取代码: "+pipeline.Coderepo+" "+pipeline.CustomGitUrl, "info", pipeline)
		time.Sleep(time.Second * 1)
		//不存在git目录
		if _, err := cli.Client.ContainerStatPath(cli.Ctx, containerID, proFolder+"/.git/config"); err != nil {
			c.RunSendInfo("git clone", "info", pipeline)
			_, _ = cli.Run(containerID, "git config --global user.email \""+UserDb.UserId+"\"")
			_, _ = cli.Run(containerID, "git config --global user.name \""+UserDb.RealName+"\"")
			line, err := cli.Run(containerID, "git clone "+oauth2Url+" .")
			c.RunSendInfo(line, "info", pipeline)
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}
		} else {
			pullShell := fmt.Sprintf("git pull origin %s:%s", pipeline.Branch, pipeline.Branch)
			c.RunSendInfo(pullShell, "info", pipeline)
			line, err := cli.Run(containerID, pullShell)
			c.RunSendInfo(line, "info", pipeline)
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}
			line, err = cli.Run(containerID, "git log -1")
			if err != nil {
				c.RunSendInfo(err.Error(), "err", pipeline)
				return
			}
			c.RunSendInfo(line, "info", pipeline)
		}

		//切换git分支
		c.RunSendInfo("切换git分支: "+pipeline.Branch, "info", pipeline)
		line, err := cli.Run(containerID, "git checkout "+pipeline.Branch)
		c.RunSendInfo(line, "info", pipeline)
		if err != nil {
			c.RunSendInfo(err.Error(), "err", pipeline)
			return
		}

		//获取gitCommit记录
		gitModel, err := model.NewUserGitlab(int(pipeline.Uid), pipeline.CustomGitUrl)
		if err != nil {
			c.OutDataJson("", iris.StatusBadRequest, err.Error())
			return
		}
		commits, _ := gitModel.GetCommits(int(pipeline.Pid), pipeline.Branch, 1, 1, "", "")
		if len(commits) > 0 {
			topCommitByte, _ := json.Marshal(commits[0])
			c.gitCommit = string(topCommitByte)
		}
	}

	//执行自定义流程
	err = c.CustomPipeline(proFolder, pipeline, cli, nodeList, runType)
	if err != nil {
		return
	}
	c.RunPosition = "finish"
	c.RunSendInfo("执行结束", "finish", pipeline)
	pipelineModel := model.NewPipeline()
	_ = pipelineModel.UpdateSuccessNum(pipeline.Uid, pipeline.ID)
	_ = pipelineModel.UpdateFields(pipeline.Uid, pipeline.ID, map[string]interface{}{"error_msg": "", "build_state": "成功"})

	defer func() {
		if pipeline.NextAction != "" {
			nextAction := strings.Split(pipeline.NextAction, ",")
			if len(nextAction) > 0 {
				req := curl.NewRequest()
				config := ymlgo.NewConfigSysBase()
				Headers := make(map[string]string)
				Headers["Content-Type"] = "application/x-www-form-urlencoded"
				Headers["Authorization"] = c.Ctx.GetHeader("Authorization")
				for _, v := range nextAction {
					_id, err := strconv.Atoi(v)
					if err == nil {
						_pipeline, err := pipelineModel.Detail(c.currentUid, uint(_id))
						if err == nil {
							go func(pipeline model.Pipeline) {
								c.RunSendInfo("触发流程: "+pipeline.Name, "info", pipeline)
								_, _ = req.SetUrl(config.Domain + "v1/pipeline/run/" + v).SetHeaders(Headers).PostUrlencoded()
							}(_pipeline)
						}
					}
				}
			}
		}
	}()
	return
}

func (c *PipelineController) GetPublicTest() {

}

//更新任意字段
func (c *PipelineController) PostConfigBy(id uint) {
	configName := c.Ctx.PostValueDefault("configName", "")
	if configName == "" {
		c.OutDataJson("", iris.StatusBadRequest, "configName is empty")
		return
	}
	configValue := c.Ctx.PostValueDefault("configValue", "")
	if configValue == "" {
		c.OutDataJson("", iris.StatusBadRequest, "configValue is empty")
		return
	}
	pipelineModel := model.NewPipeline()
	err := pipelineModel.UpdateConfig(uint(middleware.GetTokenUId(c.Ctx)), id, configName, configValue)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//更新任意字段
func (c *PipelineController) PostConfigEnvBy(id uint) {
	configName := c.Ctx.PostValueDefault("configName", "")
	if configName == "" {
		c.OutDataJson("", iris.StatusBadRequest, "configName is empty")
		return
	}
	configValue := c.Ctx.PostValueDefault("configValue", "")
	if configValue == "" {
		c.OutDataJson("", iris.StatusBadRequest, "configValue is empty")
		return
	}
	pipelineModel := model.NewPipeline()
	err := pipelineModel.SetConfigEnvBy(uint(middleware.GetTokenUId(c.Ctx)), id, configName, configValue)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson("", iris.StatusOK, "success")
	return
}

//获取当前容器运行状态
func (c *PipelineController) GetPublicStatusBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	containerName := pipeline.Language + "_" + strconv.Itoa(int(pipeline.Uid)) + "_" + strconv.Itoa(int(pipeline.ID))
	cli := util.NewDockerCli()
	containerJSON, err := cli.Client.ContainerInspect(cli.Ctx, containerName)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(iris.Map{"status": containerJSON.ContainerJSONBase.State.Status}, iris.StatusOK, "success")
	return
}

//容器运行状态列表
func (c *PipelineController) GetPublicStatusList() {
	containerNames := c.Ctx.URLParamDefault("containerNames", "")
	nameList := strings.Split(containerNames, ",")
	if len(nameList) == 0 {
		c.OutDataJson("names length is zero", iris.StatusBadRequest, "该项目未进行配置")
		return
	}
	list := make(map[string]string)
	cli := util.NewDockerCli()
	defer cli.Client.Close()
	fields := filters.NewArgs()
	for _, v := range nameList {
		fields.Add("name", v)
	}
	containers, err := cli.Client.ContainerList(cli.Ctx, types.ContainerListOptions{
		All:     true,
		Limit:   -1,
		Filters: fields,
	})

	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	for _, _name := range nameList {
		exists := false
		for _, _container := range containers {
			if _container.Names[0] == "/"+_name {
				exists = true
				list[_name] = _container.State
			}
		}

		if exists == false {
			list[_name] = "none"
		}
	}
	c.OutDataJson(list, iris.StatusOK, "success")
	return
}

//执行自定义流程
func (c *PipelineController) CustomPipeline(proFolder string, pipeline model.Pipeline, cli *util.DockerCli, nodeList []interface{}, runType map[string]string) error {
	//容器名称
	containerName := c.createContainerName(pipeline)
	detail, _ := cli.Client.ContainerInspect(cli.Ctx, containerName)
	if detail.ContainerJSONBase == nil {
		err := errors.New("该流水线容器未创建")
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}
	containerID := detail.ContainerJSONBase.ID
	err := cli.Client.ContainerStart(cli.Ctx, containerID, types.ContainerStartOptions{})
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}

	//单步执行主方法没有defer操作 所以在这里执行
	if runType["type"] != "all" {
		defer func() {
			go func() {
				_ = cli.Client.ContainerStop(cli.Ctx, containerID, nil)
				_ = cli.Client.Close()
			}()
		}()
	}

	next := false
	for k1, list := range nodeList {
		listData := list.(map[string]interface{})
		for k2, list2 := range listData {
			if k2 == "list" {
				for k3, list3 := range list2.([]interface{}) {
					v := list3.(map[string]interface{})

					//判断执行方式 卡点
					//node为手动执行 return 表现为节点闪电图标,中断执行
					if _, ok := v["monitor"]; ok && runType["type"] != "persion" {
						if v["monitor"].(string) == "manul" {
							c.RunSendInfo("monitor manul", "info", pipeline)
							return nil
						}
					}

					position := strconv.Itoa(k1) + "-" + k2 + "-" + strconv.Itoa(k3)
					c.RunPosition = position

					//单步执行
					if runType["type"] == "step" {
						if runType["position"] != position {
							continue //其他执行跳过
						}
					}

					//卡点执行 从某一步开始往下执行
					if runType["type"] == "persion" {
						if next == false {
							if runType["position"] == position {
								next = true
							}
						}

						if next == false {
							continue //跳转本次执行
						}
					}

					Node := v1Model.NewNode(cli, containerID, pipeline)

					if v["type"].(string) == "部署" {
						c.RunSendInfo("步骤: "+v["name"].(string), "info", pipeline)
						//json配置转map
						setting := v["setting"].([]interface{})
						NewSetting := make(map[string]string)
						if len(setting) > 0 {
							for _, sv := range setting {
								svValue := sv.(map[string]interface{})
								NewSetting[svValue["field"].(string)] = svValue["value"].(string)
							}
						}

						c.RunSendInfo("执行: 拷贝制品", "info", pipeline)
						typeDir := ""
						if v["name"].(string) == "主机部署" {
							typeDir = "host"
						} else if v["name"].(string)[0:6] == "Docker" {
							typeDir = "docker"
						} else if v["name"].(string) == "GitPush" {
							typeDir = "gitPush"
						} else if v["name"].(string) == "接口推送" {
							typeDir = "apiPush"
						} else {
							c.RunSendInfo("未知部署类型 ", "err", pipeline)
							return err
						}

						dirName := strings.ToLower(pipeline.Language + "_" + strconv.Itoa(int(pipeline.Uid)) + "_" + strconv.Itoa(int(pipeline.ID)))

						//物理机项目文件目录
						productsFolder := "/tmp/devops/" + dirName + "/" + typeDir

						//check setting key exists
						if err := v1Model.MapKeysExists([]string{"proName"}, NewSetting); err != nil {
							c.RunSendInfo(err.Error(), "err", pipeline)
							return err
						}
						//容器制品地址
						localFilePath := NewSetting["proName"]
						if NewSetting["proName"][:1] == "." {
							localFilePath = path.Clean(proFolder + "/" + NewSetting["proName"])
						}

						//制品地址正则匹配，如有多个，只匹配第一个
						pathStat, err := cli.Client.ContainerStatPath(cli.Ctx, containerID, localFilePath)
						if err != nil || pathStat.Name == "" {
							c.RunSendInfo("执行: 正则匹配制品名称", "info", pipeline)
							folder, fileName := filepath.Split(localFilePath)
							dstListStr, err := cli.Run(containerID, "cd "+folder+" && "+" ls | grep -E '"+fileName+"'")
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
							if dstListStr == "" {
								c.RunSendInfo("制品地址获取失败!", "err", pipeline)
								return errors.New("制品地址获取失败")
							}

							exstr := "\n"
							if dstListStr[len(dstListStr)-2:] == "\r\n" {
								exstr = "\r\n"
							}
							dstList := strings.Split(dstListStr, exstr)
							localFilePath = folder + dstList[0]
							NewSetting["proName"] = dstList[0]
						}
						//物理机项目文件绝对地址 方便构建历史存表
						c.productPath = path.Clean(productsFolder + "/" + path.Base(NewSetting["proName"]))
						err = cli.CopyFromDocker(productsFolder, localFilePath, containerID, []string{}, func(line string) {})
						if err != nil {
							c.RunSendInfo(err.Error(), "err", pipeline)
							return err
						}

						defer func() {
							go func() {
								//_ = os.RemoveAll(productsFolder)
							}()
						}()

						if v["name"].(string) == "主机部署" {
							//主机部署
							err := c.DeployHost(dirName, path.Clean(productsFolder+"/"+path.Base(NewSetting["proName"])), pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
						}

						if v["name"].(string) == "DockerSave" {
							//拷贝项目目录
							if _, ok := NewSetting["copyPro"]; ok && NewSetting["copyPro"] == "1" {
								c.RunSendInfo("执行: 拷贝项目目录", "info", pipeline)
								var excludePaths []string //需要拷贝排除的目录
								if _, ok := NewSetting["excludePaths"]; ok && NewSetting["excludePaths"] != "" {
									excludePaths = strings.Split(NewSetting["excludePaths"], "\n")
								}
								err = cli.CopyFromDocker(productsFolder, proFolder, containerID, excludePaths, func(line string) {
									c.RunSendInfo(line, "info", pipeline)
								})
								if err != nil {
									c.RunSendInfo(err.Error(), "err", pipeline)
									return err
								}
							}

							reposetoryTag, err := c.BuildImage(dirName, productsFolder, pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}

							defer func() {
								go func() {
									if _, ok := NewSetting["DockerPlatformNameSpace"]; !ok || NewSetting["DockerPlatformNameSpace"] == "" {
										NewSetting["DockerPlatformNameSpace"] = "devops"
									}
									if _, ok := NewSetting["DockerPlatformImageName"]; !ok || NewSetting["DockerPlatformImageName"] == "" {
										NewSetting["DockerPlatformImageName"] = pipeline.Name
									}

									DockerPlatformUrl := strings.Replace(strings.Replace(NewSetting["DockerPlatformUrl"], "https://", "", -1), "http://", "", -1)
									_, _ = cli.Client.ImageRemove(cli.Ctx, DockerPlatformUrl+"/"+NewSetting["DockerPlatformNameSpace"]+"/"+strings.ToLower(NewSetting["DockerPlatformImageName"])+":"+NewSetting["DockerPlatformTag"], types.ImageRemoveOptions{})
								}()
							}()

							//Docker镜像导出
							imagePath, err := c.SaveImage(dirName, reposetoryTag, pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}

							defer func() {
								//删除镜像
								go func() {
									c.once.Do(func() {
										_ = os.RemoveAll(imagePath)
									})
								}()
							}()

							//镜像部署
							err = c.DeployHost(dirName, imagePath, pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
						}

						if v["name"].(string) == "DockerPush" {
							//拷贝项目目录
							c.RunSendInfo("执行: 拷贝项目目录", "info", pipeline)
							if _, ok := NewSetting["copyPro"]; ok && NewSetting["copyPro"] == "1" {

								var excludePaths []string //需要拷贝排除的目录
								if _, ok := NewSetting["excludePaths"]; ok && NewSetting["excludePaths"] != "" {
									excludePaths = strings.Split(NewSetting["excludePaths"], "\n")
								}
								err = cli.CopyFromDocker(productsFolder, proFolder, containerID, excludePaths, func(line string) {
									c.RunSendInfo(line, "info", pipeline)
								})
								if err != nil {
									c.RunSendInfo(err.Error(), "err", pipeline)
									return err
								}
							}

							//构建镜像
							reposetoryTag, err := c.BuildImage(dirName, productsFolder, pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}

							defer func() {
								go func() {
									c.once.Do(func() {
										if _, ok := NewSetting["DockerPlatformNameSpace"]; !ok || NewSetting["DockerPlatformNameSpace"] == "" {
											NewSetting["DockerPlatformNameSpace"] = "devops"
										}
										if _, ok := NewSetting["DockerPlatformImageName"]; !ok || NewSetting["DockerPlatformImageName"] == "" {
											NewSetting["DockerPlatformImageName"] = pipeline.Name
										}
										DockerPlatformUrl := strings.Replace(strings.Replace(NewSetting["DockerPlatformUrl"], "https://", "", -1), "http://", "", -1)
										_, _ = cli.Client.ImageRemove(cli.Ctx, DockerPlatformUrl+"/"+NewSetting["DockerPlatformNameSpace"]+"/"+strings.ToLower(NewSetting["DockerPlatformImageName"])+":"+NewSetting["DockerPlatformTag"], types.ImageRemoveOptions{})
									})
								}()
							}()

							//Docker镜像push
							err = c.PushImage(reposetoryTag, pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
						}

						if v["name"].(string) == "GitPush" {
							//git部署
							err := c.GitPush(dirName, path.Clean(productsFolder+"/"+path.Base(NewSetting["proName"])), pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
						}

						if v["name"].(string) == "接口推送" {
							//接口推送
							err := c.ApiPush(dirName, path.Clean(productsFolder+"/"+path.Base(NewSetting["proName"])), pipeline, cli, NewSetting)
							if err != nil {
								c.RunSendInfo(err.Error(), "err", pipeline)
								return err
							}
						}

					} else if v["type"].(string) == "安全" {
						err := c.CodeSafe(containerID, pipeline, cli, v)
						if err != nil {
							c.RunSendInfo(err.Error(), "err", pipeline)
							return err
						}
					} else {
						setting := v["setting"].([]interface{})
						if len(setting) > 0 {
							for _, sv := range setting {
								svValue := sv.(map[string]interface{})
								//步骤名称
								if svValue["field"].(string) == "stepNum" {
									c.RunSendInfo("步骤: "+svValue["value"].(string), "info", pipeline)
								} else if svValue["field"].(string) == "readme" {
									//说明信息
								} else {
									methodName := "Common"
									//自定义Node处理方法,自定义参数 ?
									if val, ok := v["funcTag"]; ok {
										methodName = val.(string)
									}

									if _, ok := Node.Methods[methodName]; !ok {
										c.RunSendInfo("method "+methodName+"not exists!", "err", pipeline)
										return err
									}
									ret := v1Model.DynamicInvoke(Node, methodName, svValue, func(line string) {
										c.RunSendInfo(line, "info", pipeline)
									})
									if err, ok := ret[1].Interface().(error); ok && err != nil {
										c.RunSendInfo(err.Error(), "err", pipeline)
										return err
									}
									retStr := ret[0].String() //return string
									c.RunSendInfo(retStr, "info", pipeline)
								}
							}
						}
					}
				}
			}
		}
	}
	return nil
}

/*
	构建镜像
	dirName 项目生成的目录名
	productsFolder 拷贝制品物理机目录
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) BuildImage(dirName, productsFolder string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) ([]string, error) {
	c.RunSendInfo("执行: 构建镜像", "info", pipeline)
	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"proName", "dockerSelectFile", "DockerPlatformTag"}, NewSetting); err != nil {
		return []string{}, err
	}
	//替换制品地址
	NewSetting["dockerSelectFile"] = strings.Replace(NewSetting["dockerSelectFile"], "{{制品}}", filepath.Base(NewSetting["proName"]), -1) //获取文件名替换 , 会将制品拷贝到dockerfile同级目录下面

	// 替换项目目录
	ProFolder := c.getProFolder(pipeline)
	ProFolder = ProFolder[strings.LastIndex(ProFolder, "/")+1:]
	NewSetting["dockerSelectFile"] = strings.Replace(NewSetting["dockerSelectFile"], "{{项目目录}}", ProFolder, -1)

	c.RunSendInfo("DockerFile: "+NewSetting["dockerSelectFile"], "info", pipeline)
	//写入dockerfile
	tarFile := productsFolder + "/" + strings.ToLower(pipeline.Name) + ".tar"
	DockerfilePath := productsFolder + "/Dockerfile"
	err := ioutil.WriteFile(DockerfilePath, []byte(NewSetting["dockerSelectFile"]), 755)
	if err != nil {
		return []string{}, err
	}

	/*
		{
		  "docker.example.com": {
		    "username": "janedoe",
		    "password": "hunter2"
		  },
		  "https://index.docker.io/v1/": {
		    "username": "mobydock",
		    "password": "conta1n3rize14"
		  }
		}
	*/
	AuthConfigs := make(map[string]types.AuthConfig)
	AuthConfigs["https://hub.qycloud.com.cn"] = types.AuthConfig{
		Username: "pull",
		Password: "Pull123456",
	}

	//仓库命名空间目录
	if _, ok := NewSetting["DockerPlatformNameSpace"]; !ok || NewSetting["DockerPlatformNameSpace"] == "" {
		NewSetting["DockerPlatformNameSpace"] = "devops"
	}

	//镜像名称
	if _, ok := NewSetting["DockerPlatformImageName"]; !ok || NewSetting["DockerPlatformImageName"] == "" {
		NewSetting["DockerPlatformImageName"] = pipeline.Name
	}
	DockerPlatformUrl := strings.Replace(strings.Replace(NewSetting["DockerPlatformUrl"], "https://", "", -1), "http://", "", -1)
	//构建镜像
	reposetoryTag := []string{DockerPlatformUrl + "/" + NewSetting["DockerPlatformNameSpace"] + "/" + strings.ToLower(NewSetting["DockerPlatformImageName"]) + ":" + NewSetting["DockerPlatformTag"]}
	/*tar := new(archivex.TarFile)
	_ = tar.Create(tarFile)
	file, _ := os.Open(DockerfilePath)
	info, _ := file.Stat()
	_ = tar.Add(DockerfilePath, file, info)

	file2, _ := os.Open(productsFolder + "/" + path.Base(NewSetting["proName"]))
	info2, _ := file2.Stat()
	_ = tar.Add(productsFolder+"/"+path.Base(NewSetting["proName"]), file2, info2)
	//_ = tar.Close()
	*/

	err = os.MkdirAll(productsFolder+"/"+ProFolder, 755)
	if err != nil {
		return []string{}, err
	}
	tarWriter, err := util.CreateTar(productsFolder+"/"+ProFolder, tarFile)
	if err != nil {
		return []string{}, err
	}
	//写入dockerfile
	sfileInfo, err := os.Stat(DockerfilePath)
	if err != nil {
		return []string{}, err
	}
	tarWriter, err = util.TarFile("Dockerfile", DockerfilePath, sfileInfo, tarWriter)
	if err != nil {
		return []string{}, err
	}
	//写入制品
	sfileInfo, err = os.Stat(productsFolder + "/" + path.Base(NewSetting["proName"]))
	if err != nil {
		return []string{}, err
	}
	tarWriter, err = util.TarFile(path.Base(NewSetting["proName"]), productsFolder+"/"+path.Base(NewSetting["proName"]), sfileInfo, tarWriter)
	if err == nil {
		_ = tarWriter.Close()
	}
	if err != nil {
		return []string{}, err
	}

	dockerBuildContext, err := os.Open(tarFile)

	ImageBuildOptions := types.ImageBuildOptions{
		SuppressOutput: false,
		Remove:         true,
		ForceRemove:    true,
		PullParent:     true,
		Dockerfile:     "Dockerfile", // optional, is the default
		Tags:           reposetoryTag,
		AuthConfigs:    AuthConfigs,
	}

	imageBuildResponse, err := cli.Client.ImageBuild(cli.Ctx, dockerBuildContext, ImageBuildOptions)
	if err != nil {
		return []string{}, err
	}

	rd := bufio.NewReader(imageBuildResponse.Body)
	for {
		n, _, err := rd.ReadLine()
		if err != nil && err == io.EOF {
			break
		} else if err != nil {
			return []string{}, errors.New(err.Error() + " :unable to read image build response")
		}
		if strings.Contains(string(n), "errorDetail") {
			return []string{}, errors.New(string(n))
		}
		c.RunSendInfo(string(n), "info", pipeline)
	}

	defer func() {
		go func() {
			_ = dockerBuildContext.Close()
			_ = imageBuildResponse.Body.Close()
			//清除docker build 或是 pull 命令就会产生none镜像
			_, _ = util.ExecShell(`docker rmi $(docker images -f "dangling=true" -q)`)
		}()
	}()

	return reposetoryTag, nil
}

/*
	导出镜像文件
	dirName 项目生成的目录名
	reposetoryTag 本机镜像名称
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) SaveImage(dirName string, reposetoryTag []string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) (string, error) {
	c.RunSendInfo("执行: 导出镜像文件", "info", pipeline)

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"dockerExportName"}, NewSetting); err != nil {
		return "", err
	}

	img, err := cli.Client.ImageSave(cli.Ctx, reposetoryTag)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return "", err
	}
	imageFilePath := "/tmp/devops/" + dirName + "/image/"
	imageFile := imageFilePath + NewSetting["dockerExportName"]
	err = os.MkdirAll(imageFilePath, 755)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return "", err
	}
	fo, err := os.Create(imageFile)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return "", err
	}

	w := bufio.NewWriter(fo)
	_, err = io.Copy(w, img)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return "", err
	}
	if err = w.Flush(); err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return "", err
	}
	return imageFile, nil
}

/*
	push镜像
	reposetoryTag 本机镜像名称
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) PushImage(reposetoryTag []string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) error {
	c.RunSendInfo("执行: 推送镜像", "info", pipeline)

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"DockerPlatformUrlAccount", "DockerPlatformUrlPwd", "DockerPlatformUrl"}, NewSetting); err != nil {
		return err
	}

	// 权限验证
	RegistryAuth := ""
	if NewSetting["DockerPlatformUrlAccount"] != "" && NewSetting["DockerPlatformUrlPwd"] != "" {
		authConfig := types.AuthConfig{Username: NewSetting["DockerPlatformUrlAccount"], Password: NewSetting["DockerPlatformUrlPwd"]}
		encodedJSON, err := json.Marshal(authConfig)
		if err != nil {
			return err
		}
		RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
	}

	ImagePushOptions := types.ImagePushOptions{
		All:          false,
		RegistryAuth: RegistryAuth,
		Platform:     NewSetting["DockerPlatformUrl"],
	}

	if len(reposetoryTag) == 0 {
		return errors.New("reposetoryTag is 0")
	}

	if len(reposetoryTag[0]) == 0 {
		return errors.New("reposetoryTag is 0")
	}
	imagePushResponse, err := cli.Client.ImagePush(cli.Ctx, reposetoryTag[0], ImagePushOptions)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}

	rd := bufio.NewReader(imagePushResponse)
	for {
		n, _, err := rd.ReadLine()
		if err != nil && err == io.EOF {
			break
		} else if err != nil {
			c.RunSendInfo(err.Error()+" :unable to push image build response", "err", pipeline)
			return err
		}
		if strings.Contains(string(n), "errorDetail") {
			c.RunSendInfo(string(n), "err", pipeline)
			return errors.New(string(n))
		}
		c.RunSendInfo(string(n), "info", pipeline)
	}

	defer func() {
		go func() {
			//清除docker build 或是 pull 命令就会产生none镜像
			_, _ = util.ExecShell(`docker rmi $(docker images -f "dangling=true" -q)`)
		}()
	}()

	return nil
}

/*
	远程发布
	dirName 项目生成的目录名
	productsPath 拷贝制品物理机路径
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) DeployHost(dirName, productsPath string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) error {
	c.RunSendInfo("执行: 远程发布", "info", pipeline)

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"HostPort", "HostIp", "HostUser", "hostPwd", "PrivateKey", "remoteFolder", "deployCmd"}, NewSetting); err != nil {
		return err
	}

	HostPort, _ := strconv.Atoi(NewSetting["HostPort"])
	ssh := util.NewSsh(NewSetting["HostIp"], NewSetting["HostUser"], NewSetting["hostPwd"], NewSetting["PrivateKey"], HostPort)

	deployCmdShPath := "/tmp/" + dirName + ".sh"
	cmdList := []string{
		"mkdir -p " + NewSetting["remoteFolder"],
		"上传文件",
		"远程写入执行shell",
		"chmod +x " + deployCmdShPath,
		"/bin/bash " + deployCmdShPath,
	}
	for _, v := range cmdList {
		c.RunSendInfo("执行: "+v, "info", pipeline)
		if v == "上传文件" {
			err := ssh.UploadFile(productsPath, NewSetting["remoteFolder"], func(line string) {
				c.RunSendInfo(line, "info", pipeline)
			})
			if err != nil {
				return err
			}
		} else if v == "远程写入执行shell" {
			err := ssh.RemoteWriteString(v1Model.CheckShellSuccess(NewSetting["deployCmd"]), deployCmdShPath)
			if err != nil {
				return err
			}
		} else {
			err := ssh.RunMonitor(v, func(line string) {
				c.RunSendInfo(line, "info", pipeline)
			})
			if err != nil {
				return err
			}
		}
	}
	return nil
}

/*
	git发布
	dirName 项目生成的目录名
	productsPath 拷贝制品物理机路径
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) GitPush(dirName, productsPath string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) error {

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"gitRepo", "gitRepoAccount", "gitRepoPwd", "gitBranch"}, NewSetting); err != nil {
		return err
	}

	HTTPURLToRepo, _ := url.Parse(NewSetting["gitRepo"])
	gitUrl := HTTPURLToRepo.Scheme + "://" + url.QueryEscape(NewSetting["gitRepoAccount"]) + ":" + NewSetting["gitRepoPwd"] + "@" + HTTPURLToRepo.Host + HTTPURLToRepo.Path

	gitModel, err := model.NewUserGitlab(0, gitUrl)
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}

	Project := gitModel.GetGitForServerUrl(NewSetting["gitRepo"])
	if Project.ID == 0 {
		c.RunSendInfo("git仓库未找到", "err", pipeline)
		return errors.New("git仓库未找到")
	}

	//获取已存在仓库中的文件列表
	TreeNodes := []gitlab.TreeNode{}
	c.GitLabListAll(gitModel.Client, uint(Project.ID), 1, NewSetting["gitBranch"], &TreeNodes)
	TreeNodesStr := util.TreeNodesToArr(TreeNodes)

	//遍历目录
	lastProductsPath := productsPath[len(productsPath)-1:]
	if lastProductsPath == "/" {
		productsPath = productsPath[:len(productsPath)-1]
	}
	filesList := make([]string, 0, 30)
	err = filepath.Walk(productsPath, func(filename string, fi os.FileInfo, err error) error {
		if err != nil { //忽略错误
			return err
		}
		if fi.IsDir() { // 忽略目录
			return nil
		}
		if strings.Index(filename, "/.git/") != -1 { //忽略git目录
			return nil
		}
		if strings.Index(filename, "/__MACOSX/") != -1 { //忽略Mac目录
			return nil
		}
		filesList = append(filesList, strings.Replace(filename, productsPath+"/", "", -1))
		return nil
	})
	if err != nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}

	//空目录
	if len(filesList) == 0 {
		c.RunSendInfo("制品目录中未包含文件", "err", pipeline)
		return err
	}

	//需要删除的git仓库 文件
	DifferenceList := util.Difference(TreeNodesStr, filesList)

	//组织差异文件
	actions := []*gitlab.CommitAction{}
	for _, v := range filesList {
		flag := true //默认新建
		for _, vv := range TreeNodes {
			if v == vv.Path {
				flag = false //存在即 update
			}
		}
		_content, _ := ioutil.ReadFile(productsPath + "/" + v)
		encodeString := base64.StdEncoding.EncodeToString(_content)
		if flag == true {
			actions = append(actions, &gitlab.CommitAction{
				Action:   "create",
				FilePath: v,
				Content:  encodeString,
				Encoding: "base64",
			})
		} else {
			actions = append(actions, &gitlab.CommitAction{
				Action:   "update",
				FilePath: v,
				Content:  encodeString,
				Encoding: "base64",
			})
		}
	}

	for _, diffPath := range DifferenceList {
		actions = append(actions, &gitlab.CommitAction{
			Action:   "delete",
			FilePath: diffPath,
		})
	}

	//推送git 模拟触发gitlab push操作
	CommitOptions := &gitlab.CreateCommitOptions{
		Branch:        gitlab.String(NewSetting["gitBranch"]),
		Actions:       actions,
		CommitMessage: gitlab.String("花木兰devops自动推送"),
	}

	_, _, err = gitModel.Client.Commits.CreateCommit(Project.ID, CommitOptions)
	if err != nil {
		c.RunSendInfo("导入推送失败", "err", pipeline)
		return err
	}
	TreeNodes = []gitlab.TreeNode{}
	actions = []*gitlab.CommitAction{}
	return nil
}

/*
	api接口推送
	dirName 项目生成的目录名
	productsPath 拷贝制品物理机路径
	pipeline 模型
	cli Docker接口
	setting 配置
*/
func (c *PipelineController) ApiPush(dirName, productsPath string, pipeline model.Pipeline, cli *util.DockerCli, NewSetting map[string]string) error {
	c.RunSendInfo("执行: api接口推送", "info", pipeline)
	if !util.FileExists(productsPath) {
		return errors.New("制品 " + productsPath + " 不存在")
	}

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"apiBody", "apiMethod", "apiUrl", "apiHeaders"}, NewSetting); err != nil {
		return err
	}

	//环境变量
	envsMap := make(map[string]string)
	if string(pipeline.Envs.([]byte)) != "" {
		var Envs []map[string]string
		err := json.Unmarshal(pipeline.Envs.([]byte), &Envs)
		if err != nil {
			return err
		}
		for _, v := range Envs {
			envsMap[`{{`+string(v["name"])+`}}`] = v["value"]
		}
	}

	//设置body参数
	payload := &bytes.Buffer{}
	apiBodyWriter := multipart.NewWriter(payload)
	if NewSetting["apiBody"] != "" {
		apiBody := strings.Split(NewSetting["apiBody"], "\n")
		for _, v := range apiBody {
			bodyTmp := strings.Split(v, ":")
			if len(bodyTmp) == 2 {
				if len(envsMap) > 0 {
					for envName, envVal := range envsMap {
						bodyTmp[1] = strings.Replace(bodyTmp[1], envName, envVal, -1)
					}
				}
				_ = apiBodyWriter.WriteField(bodyTmp[0], bodyTmp[1])
			}
		}
	}

	//todo 开放平台定制 mv制品地址
	if c.accessToken != "" {
		proDir, proFile := filepath.Split(productsPath)
		proExt := filepath.Ext(proFile)
		pipelineLogModel := model.NewPipelineLogs()
		count := strconv.Itoa(int(pipelineLogModel.Count(pipeline.ID) + 1))
		gitUrl := pipeline.Coderepo
		if pipeline.CustomGitUrl != "" {
			gitUrl = pipeline.CustomGitUrl
		}
		NewFileName := strings.TrimSuffix(filepath.Base(gitUrl), filepath.Ext(gitUrl)) + "_" + count + proExt
		NewFilePath := proDir + NewFileName
		err := util.CopyFile(productsPath, NewFilePath)
		if err != nil {
			return err
		}
		productsPath = NewFilePath
		c.productPath = productsPath
	}

	file, errFile := os.Open(productsPath)
	defer file.Close()
	part, errFile := apiBodyWriter.CreateFormFile("proName", filepath.Base(productsPath))
	_, errFile = io.Copy(part, file)
	if errFile != nil {
		return errFile
	}
	err := apiBodyWriter.Close()
	if err != nil {
		return err
	}

	client := &http.Client{
		Timeout: 5 * time.Second,
		Transport: &http.Transport{ //解决x509: certificate signed by unknown authority
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
		CheckRedirect: func(req *http.Request, via []*http.Request) error { //禁止重定向
			return http.ErrUseLastResponse
		},
	}
	req, err := http.NewRequest(NewSetting["apiMethod"], NewSetting["apiUrl"], payload)

	if err != nil {
		return err
	}
	//设置header参数
	if NewSetting["apiHeaders"] != "" {
		apiHeaders := strings.Split(NewSetting["apiHeaders"], "\n")
		for _, v := range apiHeaders {
			headerTmp := strings.Split(v, ":")
			if len(headerTmp) == 2 {
				if len(envsMap) > 0 {
					for envName, envVal := range envsMap {
						headerTmp[1] = strings.Replace(headerTmp[1], envName, envVal, -1)
					}
					req.Header.Add(headerTmp[0], headerTmp[1])
				}
			}
		}
	}
	req.Header.Set("Content-Type", apiBodyWriter.FormDataContentType())
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}
	if string(body) != "success" {
		return errors.New(string(body))
	}
	c.RunSendInfo(string(body), "info", pipeline)
	return nil
}

/*
sonarqube代码检测
*/
func (c *PipelineController) CodeSafe(containerID string, pipeline model.Pipeline, cli *util.DockerCli, Node map[string]interface{}) error {
	c.RunSendInfo("步骤: "+Node["name"].(string), "info", pipeline)
	setting := Node["setting"].([]interface{})
	NewSetting := make(map[string]string)
	if len(setting) > 0 {
		for _, sv := range setting {
			svValue := sv.(map[string]interface{})
			if svValue["value"] != nil {
				NewSetting[svValue["field"].(string)] = svValue["value"].(string)
			}
		}
	}

	//check setting key exists
	if err := v1Model.MapKeysExists([]string{"sonarCode", "qualitySelect"}, NewSetting); err != nil {
		return err
	}

	//环境安装
	/*
		    https://blog.csdn.net/weixin_44410537/article/details/113770515
			文件名： sonar-project.properties
			sonar.projectKey=name // 自己的sonar服务器上注册的项目名称
			sonar.projectName=name // 自己的sonar服务器上注册的项目名称 将在 Web 界面上显示的项目的名称
			sonar.projectVersion=1.0 //项目版本 对应 Maven 项目
			sonar.sources=src // 检查的代码范围
			sonar.exclusions=node_modules\**\* // 忽略文件
			sonar.host.url= localhost // 自己的sonar服务器地址包括端口名
			sonar.login=username // 自己的sonar服务器用户名
			sonar.password=password // 自己的sonar服务器密码
			sonar.sourceEncoding=UTF-8*/
	config := ymlgo.NewConfigSysBase()
	scanCmd := NewSetting["sonarCode"]
	scanCmd = strings.Replace(scanCmd, "{projectKey}", pipeline.Name, -1)
	scanCmd = strings.Replace(scanCmd, "{projectName}", pipeline.Name, -1)
	scanCmd = strings.Replace(scanCmd, "{url}", config.SonarHost, -1)
	scanCmd = strings.Replace(scanCmd, "{login}", config.SonarToken, -1)
	if Node["name"].(string) != "mvn代码质量检测" {
		//1.拷贝二进制 2.设置全局变量
		if _, err := cli.Client.ContainerStatPath(cli.Ctx, containerID, "/tmp/sonar-scaner-4.4.0/bin/sonar-scanner"); err != nil {
			c.RunSendInfo("拷贝sonar压缩包", "info", pipeline)
			if err := cli.CopyToDocker("/uploads/download/sonar-scaner-4.4.0.tar.gz", "/tmp/sonar-scaner-4.4.0", containerID); err != nil {
				return err
			}
			c.RunSendInfo("解压sonar二进制文件", "info", pipeline)
			_, _ = cli.Run(containerID, "cd /tmp/sonar-scaner-4.4.0 && tar -xzvf sonar-scaner-4.4.0.tar.gz")
		}
		scanCmd = "export PATH=/tmp/sonar-scaner-4.4.0/bin:$PATH && " + scanCmd
	}

	//项目创建
	SonarObj, err := v1Model.NewSonar()
	if err != nil {
		return err
	}

	ProjectName := ""
	projectList, _, err := SonarObj.Cli.Projects.Search(&sonargo.ProjectsSearchOption{Projects: pipeline.Name})
	if err != nil {
		return err
	}
	if len(projectList.Components) > 0 {
		c.RunSendInfo("sonar项目已存在", "info", pipeline)
		ProjectName = projectList.Components[0].Name
	} else {
		c.RunSendInfo("创建sonar项目", "info", pipeline)
		projectOne, _, err := SonarObj.Cli.Projects.Create(&sonargo.ProjectsCreateOption{
			Name:       pipeline.Name,
			Project:    pipeline.Name,
			Visibility: "public",
		})
		if err != nil {
			return err
		}
		ProjectName = projectOne.Project.Name
	}

	//阀值修改
	_, err = SonarObj.Cli.Qualitygates.Select(&sonargo.QualitygatesSelectOption{
		GateId:     NewSetting["qualitySelect"],
		ProjectKey: ProjectName,
	})
	if err != nil {
		return errors.New("质量阀更新失败 :" + err.Error())
	}

	qualitygates, _, err := SonarObj.Cli.Qualitygates.GetByProject(&sonargo.QualitygatesGetByProjectOption{
		Project: ProjectName,
	})
	if err != nil {
		return errors.New("质量阀获取失败 :" + err.Error())
	}
	c.RunSendInfo("当前质量阀 :"+qualitygates.QualityGate.Name, "info", pipeline)

	t1 := time.Now()
	//命令执行
	c.RunSendInfo("sonar项目检测开始", "info", pipeline)
	scanCmdRs, err := cli.Exec(containerID, scanCmd, func(line string) {
		c.RunSendInfo(line, "info", pipeline)
	})

	if err != nil {
		return err
	}

	if strings.Contains(scanCmdRs, "BUILD FAILURE") {
		return errors.New(scanCmdRs[strings.Index(scanCmdRs, "BUILD FAILURE"):])
	}

	//根据时间对比是否最新记录
	c.RunSendInfo("等待sonar检测数据后台处理...", "info", pipeline)
	waitTime := 300
	for i := 1; i <= waitTime; i++ {
		time.Sleep(time.Second * 5)
		c.RunSendInfo(fmt.Sprintf("等待 %d 秒", i*5), "info", pipeline)
		showObject, _, err := SonarObj.Cli.Components.Show(&sonargo.ComponentsShowOption{Component: ProjectName})
		if err != nil {
			c.RunSendInfo(err.Error(), "err", pipeline)
			return err
		}

		if showObject.Component == nil {
			continue
		}

		if showObject.Component.AnalysisDate == "" {
			continue
		}

		t2, _ := time.Parse("2006-01-02T15:04:05+0000", showObject.Component.AnalysisDate)
		if t2.After(t1) {
			c.RunSendInfo(fmt.Sprintf("获取到最新检测记录时间: %s", t2.Local().Format("2006-01-02 15:04:05")), "info", pipeline)
			break
		}
	}

	//获取检测数据
	projectStatusObject, _, err := SonarObj.Cli.Qualitygates.ProjectStatus(&sonargo.QualitygatesProjectStatusOption{ProjectKey: ProjectName})
	if projectStatusObject == nil {
		c.RunSendInfo(err.Error(), "err", pipeline)
		return err
	}
	ProjectStatusStr := projectStatusObject.ProjectStatus.Status
	componentObject, _, err := SonarObj.Cli.Measures.Component(&sonargo.MeasuresComponentOption{Component: ProjectName, MetricKeys: "new_bugs,bugs,new_coverage,coverage,new_vulnerabilities,vulnerabilities,new_duplicated_lines_density,duplicated_lines_density"})
	if err != nil {
		return err
	}
	MeasuresMap := make(map[string]string)
	if len(componentObject.Component.Measures) > 0 {
		MeasuresMap["duplicated_lines_density"] = "重复(%)"
		MeasuresMap["vulnerabilities"] = "漏洞"
		MeasuresMap["new_vulnerabilities"] = "新增漏洞"
		MeasuresMap["bugs"] = "Bug"
		MeasuresMap["new_bugs"] = "新增Bug"
		MeasuresMap["coverage"] = "覆盖率(%)"
		MeasuresMap["new_coverage"] = "新增覆盖率(%)"
		MeasuresStrMap := make(map[string]string)
		for _, v := range componentObject.Component.Measures {
			if _, ok := MeasuresMap[v.Metric]; ok {
				MeasuresStrMap[MeasuresMap[v.Metric]] = v.Value
			} else {
				MeasuresStrMap[v.Metric] = v.Value
			}
		}
		MeasuresStrMapJson, err := json.Marshal(MeasuresStrMap)
		if err != nil {
			c.RunSendInfo(err.Error(), "err", pipeline)
			return err
		}
		c.RunSendInfo("代码检测结果【"+ProjectStatusStr+"】", "info", pipeline)
		c.RunSendInfo(string(MeasuresStrMapJson), "info", pipeline)
	}

	c.RunSendInfo("质量报告地址: "+config.SonarHost+"/dashboard?id="+pipeline.Name, "info", pipeline)

	if ProjectStatusStr == "ERROR" {
		return errors.New("代码检测未通过，当前检测状态【" + ProjectStatusStr + "】")
	}
	return nil
}

//递归获取已存在仓库中的文件列表
func (c *PipelineController) GitLabListAll(git *gitlab.Client, ProjectId uint, page uint, ref string, list *[]gitlab.TreeNode) {
	ListTreeOptions := &gitlab.ListTreeOptions{
		ListOptions: gitlab.ListOptions{
			Page:    int(page),
			PerPage: 100,
		},
		Recursive: gitlab.Bool(true),
		Ref:       gitlab.String(ref),
	}
	TreeNodes, _, _ := git.Repositories.ListTree(int(ProjectId), ListTreeOptions)
	if len(TreeNodes) > 0 {
		for _, v := range TreeNodes {
			*list = append(*list, *v)
		}
		c.GitLabListAll(git, ProjectId, page+1, ref, list)
	}
}

//获取映射目录
func (c *PipelineController) getProFolder(pipeline model.Pipeline) string {
	currentUser, err := user.Current()
	if err != nil {
		//c.RunSendInfo("获取当前机器的用户信息失败:"+err.Error(), "err", pipeline)
		return "/tmp/devops/user_" + strconv.Itoa(int(pipeline.Uid)) + "/project_" + strconv.Itoa(int(pipeline.ID))
	}
	proFolder := currentUser.HomeDir + "/devops/user_" + strconv.Itoa(int(pipeline.Uid)) + "/project_" + strconv.Itoa(int(pipeline.ID))
	return proFolder
}

//根据规则生成容器名称
func (c *PipelineController) createContainerName(pipeline model.Pipeline) string {
	containerName := pipeline.Language + "_" + strconv.Itoa(int(pipeline.Uid)) + "_" + strconv.Itoa(int(pipeline.ID))
	return containerName
}

//输出运行结果
func (c *PipelineController) RunSendInfo(msg string, level string, pipeline model.Pipeline, ignoreLogs ...bool) {
	PipelineLogs := model.NewPipelineLogs()
	pipelineModel := model.NewPipeline()
	userModel := model.NewUser()

	//回调推送
	if level != "info" && pipeline.CallbackUrl != "" {
		go func() {
			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), ": CallbackUrl access_token:", c.accessToken)
			_, _ = curl.NewRequest().SetUrl(pipeline.CallbackUrl).SetHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded", "access_token": c.accessToken}).SetPostData(map[string]interface{}{"id": strconv.Itoa(int(pipeline.ID)), "name": pipeline.Name, "level": level, "msg": msg, "env": string(pipeline.Envs.([]byte))}).PostUrlencoded()
		}()
	}

	//获取缓存 避免每次查询数据库
	touser := ""
	cacheKey := "RunSendInfo" + strconv.Itoa(int(pipeline.Uid))
	userIdCache, found := libs.GetCache(cacheKey)
	if found {
		touser = userIdCache.(string)
	} else {
		UserDb, _ := userModel.GetUser(pipeline.Uid)
		touser = UserDb.UserId
		libs.SetCacheDefault(cacheKey, touser, 30*time.Second)
	}

	insertId := c.logsId
	//组织日志信息
	if len(ignoreLogs) == 0 {
		c.lock.Lock()
		_ = util.Tracefile(c.FullLogPath, msg+"\n")
		c.lock.Unlock()
	}

	//日志记录
	if level == "err" {
		_ = pipelineModel.UpdateErrNum(pipeline.Uid, pipeline.ID)
		_ = pipelineModel.UpdateFields(pipeline.Uid, pipeline.ID, map[string]interface{}{"error_msg": msg, "build_state": "失败"})
	}

	//写入日志
	if level != "info" {
		UpdateFields := make(map[string]interface{})
		UpdateFields["gitCommit"] = c.gitCommit
		UpdateFields["monitor"] = c.Monitor
		UpdateFields["speed"] = time.Now().Sub(c.StartTime).String()
		UpdateFields["error_position"] = c.RunPosition
		if level == "err" {
			UpdateFields["cate"] = 2
			UpdateFields["errMsg"] = msg
		} else if level == "finish" {
			UpdateFields["cate"] = 1
		}

		//todo 开放平台定制, 通过全局环境变量Env中的zipName参数名判断是否存在，存在则入库
		zipName := pipelineModel.GetConfigEnvBy(c.currentUid, pipeline.ID, "zipName")
		if zipName != "" {
			UpdateFields["zipName"] = zipName
		}

		//记录制品包地址，已供下载
		if c.productPath != "" && util.FileExists(c.productPath) {
			pwdDir, _ := os.Getwd()
			dstPath := pwdDir + "/uploads/products/" + strconv.Itoa(int(pipeline.ID)) + "/" + time.Now().Format("20060102150405") + "_" + filepath.Base(c.productPath)
			err := util.CopyFile(c.productPath, dstPath)
			if err == nil {
				UpdateFields["zipPath"] = strings.Replace(dstPath, pwdDir, "", 1)
			}
		}

		_ = PipelineLogs.UpdateFields(uint(insertId), UpdateFields)

		if level == "err" && pipeline.SendEmail != "" {
			go func() {
				emailSubject := fmt.Sprintf("《%s》流水线执行异常报错", pipeline.Name)
				emailBody := fmt.Sprintf("错误信息:%s <br /> 错误日志见附件", msg)
				_ = libs.SendEmail(pipeline.SendEmail, emailSubject, emailBody, c.FullLogPath)
			}()
		}
	}

	messageJson, _ := json.Marshal(iris.Map{"level": level, "msg": msg, "position": c.RunPosition, "id": pipeline.ID})
	_ = middleware.GetPipelineWsServer().PushMessageAll(string(messageJson))
}

//todo 获取开放平台包的名称
func (c *PipelineController) GetKfptPackNameBy(name string) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.DetailByName(uint(middleware.GetTokenUId(c.Ctx)), name)
	if err != nil {
		c.OutDataJson(0, iris.StatusBadRequest, err.Error())
		return
	}
	pipelineLogModel := model.NewPipelineLogs()
	count := strconv.Itoa(int(pipelineLogModel.Count(pipeline.ID)))
	gitUrl := pipeline.Coderepo
	if pipeline.CustomGitUrl != "" {
		gitUrl = pipeline.CustomGitUrl
	}
	NewFileName := strings.TrimSuffix(filepath.Base(gitUrl), filepath.Ext(gitUrl)) + "_" + time.Now().Format("20060102") + "_" + count
	NewFileName = strings.Replace(NewFileName, "_source", "", -1)
	c.OutDataJson(NewFileName, iris.StatusOK, "success")

}

//导出配置
func (c *PipelineController) GetExportSettingBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	if pipeline.Flow == "" || pipeline.Flow == "[]" {
		c.OutDataJson("", iris.StatusBadRequest, "该项目未进行配置")
		return
	}

	setting := make(map[string]interface{})
	setting["flow"] = string(pipeline.Flow.([]byte))
	setting["envs"] = string(pipeline.Envs.([]byte))
	setting["Setting_file"] = string(pipeline.SettingFile.([]byte))

	settingByte, err := json.Marshal(setting)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	encodeString := base64.RawURLEncoding.EncodeToString(settingByte)
	c.OutDataJson(encodeString, iris.StatusOK, "success")
}

//导入配置
func (c *PipelineController) PostImportSetting() {
	files, _, err := c.Ctx.FormFile("file")
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	content, err := ioutil.ReadAll(files)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	settingStr := string(content)
	if settingStr == "" {
		c.OutDataJson("", iris.StatusBadRequest, "配置内容为空")
		return
	}
	decodeBytes, err := base64.RawURLEncoding.DecodeString(settingStr)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "解析数据失败")
		return
	}
	var tempMap map[string]interface{}
	err = json.Unmarshal(decodeBytes, &tempMap)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, "解析数据失败")
		return
	}

	if len(tempMap) != 3 {
		c.OutDataJson("", iris.StatusBadRequest, "数据格式错误!")
		return
	}

	if _, ok := tempMap["flow"]; !ok {
		c.OutDataJson("", iris.StatusBadRequest, "数据格式错误!")
		return
	}

	if _, ok := tempMap["envs"]; !ok {
		c.OutDataJson("", iris.StatusBadRequest, "数据格式错误!")
		return
	}

	if _, ok := tempMap["Setting_file"]; !ok {
		c.OutDataJson("", iris.StatusBadRequest, "数据格式错误!")
		return
	}

	/*if err := pipelineModel.UpdateFields(uid, id, tempMap); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}*/
	c.OutDataJson(tempMap, iris.StatusOK, "success")
}

//保存阶段模板
func (c *PipelineController) PostTemplate() {
	data := c.Ctx.PostValueDefault("data", "")
	if data == "" {
		c.OutDataJson("", iris.StatusBadRequest, "json is empty")
		return
	}
	c.lock.Lock()
	err := ioutil.WriteFile("./config/NodeTemplate.json", []byte(data), 755)
	c.lock.Unlock()
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson("", iris.StatusOK, "success")
}

//获取阶段模板
func (c *PipelineController) GetTemplate() {
	_byte, err := ioutil.ReadFile("./config/NodeTemplate.json")
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(string(_byte), iris.StatusOK, "success")
}

//获取阶段模板
func (c *PipelineController) GetPublicTemplate() {
	_byte, err := ioutil.ReadFile("./config/NodeTemplate.json")
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(string(_byte), iris.StatusOK, "success")
}

//预设流水线列表模板
func (c *PipelineController) GetPublicFlowTemplate() {
	FlowModel := model.NewFlow()
	list := FlowModel.ListAll()
	c.OutDataJson(list, iris.StatusOK, "success")
}

//预设流水线列表
func (c *PipelineController) GetFlowList() {
	page, err := strconv.Atoi(c.Ctx.URLParam("page"))
	if err != nil || page < 1 {
		page = 1
	}
	pageSize, err := strconv.Atoi(c.Ctx.URLParam("pageSize"))
	if err != nil || pageSize < 1 {
		pageSize = 20
	}
	pipelineModel := model.NewFlow()
	list, total, pages := pipelineModel.List(uint(page), uint(pageSize))
	c.OutDataJson(iris.Map{"list": list, "total": total, "pages": pages, "currentPage": page}, iris.StatusOK, "success")
}

//预设流水线创建
func (c *PipelineController) PostFlowCreate() {
	pipelineModel := model.NewFlow()
	if err := c.Ctx.ReadJSON(pipelineModel); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	err := pipelineModel.Create()
	if err == nil {
		c.OutDataJson("", iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//预设流水线修改
func (c *PipelineController) PostFlowUpdate() {
	pipelineModel := model.NewFlow()
	if err := c.Ctx.ReadJSON(pipelineModel); err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	err := pipelineModel.Update()
	if err == nil {
		c.OutDataJson("", iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//预设流水线详情
func (c *PipelineController) GetFlowDetailBy(id uint) {
	pipelineModel := model.NewFlow()
	pipeline, err := pipelineModel.Detail(id)
	if err == nil {
		c.OutDataJson(pipeline, iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//预设流水线删除
func (c *PipelineController) PostFlowDelBy(id uint) {
	pipelineModel := model.NewFlow()
	err := pipelineModel.Del(id)
	if err == nil {
		c.OutDataJson("", iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//管理人员修改
func (c *PipelineController) PostManageUsersBy(pid uint) {
	pipelineModel := model.NewPipeline()
	manageUsers := c.Ctx.PostValueDefault("manageUsers", "")
	err := pipelineModel.UpdateFields(uint(middleware.GetTokenUId(c.Ctx)), pid, map[string]interface{}{"manage_uid": manageUsers})
	if err == nil {
		c.OutDataJson("", iris.StatusOK, "success")
	} else {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
	}
	return
}

//流水线日志列表
func (c *PipelineController) GetLogListBy(pid uint) {
	page, err := strconv.Atoi(c.Ctx.URLParam("page"))
	if err != nil || page < 1 {
		page = 1
	}
	pageSize, err := strconv.Atoi(c.Ctx.URLParam("pageSize"))
	if err != nil || pageSize < 1 {
		pageSize = 20
	}
	pipelineLogsModel := model.NewPipelineLogs()
	list, total, pages := pipelineLogsModel.List(pid, uint(page), uint(pageSize))
	c.OutDataJson(iris.Map{"list": list, "total": total, "pages": pages, "currentPage": page}, iris.StatusOK, "success")
	return
}

//流水线日志数量统计
func (c *PipelineController) GetLogCountBy(name string) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.DetailByName(uint(middleware.GetTokenUId(c.Ctx)), name)
	if err != nil {
		c.OutDataJson(0, iris.StatusBadRequest, err.Error())
		return
	}
	pipelineLogModel := model.NewPipelineLogs()
	count := pipelineLogModel.Count(pipeline.ID)
	c.OutDataJson(count, iris.StatusOK, "success")
}

//todo 开放平台。流水线日志创建
func (c *PipelineController) PostLogCreateBy(id uint) {
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.Detail(uint(middleware.GetTokenUId(c.Ctx)), id)
	if err != nil || pipeline.ID == 0 {
		c.OutDataJson("", iris.StatusBadRequest, "记录未找到")
		return
	}

	zipName := c.Ctx.PostValueDefault("zipName", "")
	if zipName == "" {
		c.OutDataJson("", iris.StatusBadRequest, "zipName is empty")
		return
	}

	monitor := c.Ctx.PostValueDefault("monitor", "")
	if monitor == "" || (monitor != "auto" && monitor != "manul" && monitor != "import") {
		c.OutDataJson("", iris.StatusBadRequest, "monitor is error")
		return
	}

	cate := c.Ctx.PostValueIntDefault("cate", 0)
	if cate == 0 || (cate != 1 && cate != 2 && cate != 3) {
		c.OutDataJson("", iris.StatusBadRequest, "cate is error")
		return
	}

	PipelineLogs := model.NewPipelineLogs()
	userModel := model.NewUser()
	userInfo, _ := userModel.GetUserByUserId(middleware.GetTokenUserId(c.Ctx))
	PipelineLogs.UserId = userInfo.UserId
	PipelineLogs.RealName = userInfo.RealName
	PipelineLogs.Coderepo = pipeline.Coderepo
	if pipeline.CustomGitUrl != "" {
		PipelineLogs.Coderepo = pipeline.CustomGitUrl
	}
	PipelineLogs.Branch = pipeline.Branch
	PipelineLogs.Pid = pipeline.ID
	PipelineLogs.Cate = uint(cate)
	PipelineLogs.Monitor = monitor
	PipelineLogs.ZipName = zipName
	PipelineLogs.Speed = `0s`
	PipelineLogs.ErrorPosition = `finish`
	insertId, err := PipelineLogs.Create()
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(insertId, iris.StatusOK, "success")
}

//todo 开放平台。流水线日志列表
func (c *PipelineController) GetLogListOpenidBy(name string) {
	page, err := strconv.Atoi(c.Ctx.URLParam("page"))
	if err != nil || page < 1 {
		page = 1
	}
	pageSize, err := strconv.Atoi(c.Ctx.URLParam("pageSize"))
	if err != nil || pageSize < 1 {
		pageSize = 20
	}
	pipelineModel := model.NewPipeline()
	pipeline, err := pipelineModel.DetailByName(uint(middleware.GetTokenUId(c.Ctx)), name)
	if err != nil {
		c.OutDataJson(iris.Map{"list": nil, "total": 0, "pages": 0, "currentPage": page}, iris.StatusOK, err.Error())
		return
	}
	pipelineLogsModel := model.NewPipelineLogs()

	searchMap := make(map[string]string)
	searchMap["cate"] = c.Ctx.URLParamDefault("cate", "")
	searchMap["zipName"] = c.Ctx.URLParamDefault("zipName", "")

	list, total, pages := pipelineLogsModel.List(pipeline.ID, uint(page), uint(pageSize), searchMap)

	for k, v := range list {
		re, err := regexp.Compile("oauth2:[a-zA-Z0-9-_]+@")
		if err == nil {
			list[k].Coderepo = re.ReplaceAllString(v.Coderepo, "")
		}
	}

	c.OutDataJson(iris.Map{"list": list, "total": total, "pages": pages, "currentPage": page}, iris.StatusOK, "success")
	return
}

//流水线日志下载
func (c *PipelineController) GetLogDetailBy(id uint) {
	pipelineLogsModel := model.NewPipelineLogs()
	data, err := pipelineLogsModel.Detail(id)
	if err != nil || data.ID == 0 {
		c.OutDataJson("", iris.StatusBadRequest, "记录未找到")
		return
	}
	if !util.FileExists(data.Message) {
		c.OutDataJson("", iris.StatusBadRequest, "日志文件不存在")
		return
	}
	config := ymlgo.NewConfigSysBase()
	downLoadUrl := config.Domain[:len(config.Domain)-1] + data.Message[1:]
	contentByte, err := ioutil.ReadFile(data.Message)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(downLoadUrl, iris.StatusOK, "success", string(contentByte))
	return
}

//获取默认docker列表
func (c *PipelineController) GetPublicDockerFile() {
	dockerFilePath := "./config/dockerFile"
	f, err := os.OpenFile(dockerFilePath, os.O_RDONLY, os.ModeDir)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	//延迟关闭目录
	defer f.Close()
	fileInfo, err := f.Readdir(-1)
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}

	list := make(map[string]string)
	for _, info := range fileInfo {
		content, _ := ioutil.ReadFile(dockerFilePath + "/" + info.Name())
		list[info.Name()] = string(content)
	}
	c.OutDataJson(list, iris.StatusOK, "success")
}

//语言列表
func (c *PipelineController) GetPublicLanguageList() {
	str, err := ioutil.ReadFile("./config/languageOptions.json")
	if err != nil {
		c.OutDataJson("", iris.StatusBadRequest, err.Error())
		return
	}
	c.OutDataJson(string(str), iris.StatusOK, "success")
}
